/**
 * HTTP Client Module
 * This module provides a stateless, function-based API for making HTTP requests.
 * Each request function (get, post, request, etc.) creates an isolated,
 * short-lived native client instance, ensuring thread-safety and preventing state conflicts.
 *
 * Features:
 * - GET/POST/PUT/PATCH/DELETE requests
 * - File upload/download with progress
 * - HTTPS with certificate verification options (disabled by default)
 * - Fully configurable via an options object per request
 * 
 * Usage:
 * - Simple requests: `httpclient.get(url, { timeout: 3000 })`
 * - Complex requests: `httpclient.request({ url, method, headers: { 'Content-Type': 'application/json' }, body, ... })`
 */
import * as native from './libvbar-m-dxhttpclient.so'

const httpclient = {}

/**
 * The core request function. Each call invokes the native stateless request function.
 * @param {object} options - Request options.
 * @param {string} options.url - The request URL. (Required)
 * @param {string} [options.method='GET'] - The request method (GET, POST, etc.).
 * @param {object<string, string>|string[]} [options.headers] - Request headers as a key-value object (recommended) or an array of strings for backward compatibility.
 * @param {string|object} [options.body] - The request body. JS objects will be stringified to JSON.
 * @param {number} [options.timeout=5000] - Timeout in milliseconds.
 * @param {Function} [options.onProgress] - Progress callback function.
 * @param {number} [options.verifyPeer] - Certificate verification (0: disable, 1: enable). Default: 0 (disabled for convenience in IoT).
 * @param {number} [options.verifyHost] - Hostname verification (0: disable, 2: enable). Default: 0 (disabled for convenience in IoT).
 * @param {string} [options.caFile] - Path to CA certificate file.
 * @returns {object} Response result { code, status, message, headers, data }.
 *                   - headers: Object containing response headers (keys are lowercase).
 * @throws {Error} Throws error on invalid input.
 */
httpclient.request = function (options) {
    if (!options || typeof options !== 'object') {
        throw new Error("Request options object is required");
    }
    if (!options.url) {
        throw new Error("options.url is required");
    }

    const optsToSet = { ...options };

    // Default method to GET
    if (!optsToSet.method) {
        optsToSet.method = 'GET';
    }

    // Default timeout
    if (optsToSet.timeout === undefined) {
        optsToSet.timeout = 5000;
    }

    // Convert headers object to array of strings for C layer BEFORE further processing
    if (optsToSet.headers && typeof optsToSet.headers === 'object' && !Array.isArray(optsToSet.headers)) {
        optsToSet.headers = Object.entries(optsToSet.headers).map(([key, value]) => `${key}: ${value}`);
    }
    if (!optsToSet.headers) {
        optsToSet.headers = [];
    }
    // Auto-stringify JSON body and set Content-Type header
    if (optsToSet.body && typeof optsToSet.body === 'object') {
        optsToSet.body = JSON.stringify(optsToSet.body);
        // Add header only if not already present
        if (!optsToSet.headers.some(h => h.toLowerCase().startsWith('content-type:'))) {
            optsToSet.headers.push('Content-Type: application/json');
        }
    }

    return native.request(optsToSet);
}

/**
 * Send GET request.
 * @param {string} url - Request URL.
 * @param {number} [timeout=5000] - Timeout in milliseconds. For backward compatibility.
 * @param {object} [options] - Additional request options (headers, etc.).
 * @returns {object} Response result.
 */
httpclient.get = function (url, timeout = 5000, options = {}) {
    return httpclient.request({ ...options, url: url, method: 'GET', timeout: timeout });
}

/**
 * Send POST request with a body.
 * @param {string} url - Request URL.
 * @param {string|object} data - Request body. JS objects will be stringified as JSON.
 * @param {number} [timeout=5000] - Timeout in milliseconds. For backward compatibility.
 * @param {object} [options] - Additional request options (headers, timeout, etc.).
 * @returns {object} Response result.
 */
httpclient.post = function (url, data, timeout = 5000, options = {}) {
    return httpclient.request({ ...options, url: url, method: 'POST', body: data, timeout: timeout });
}

/**
 * Send PUT request with a body.
 * @param {string} url - Request URL.
 * @param {string|object} data - Request body. JS objects will be stringified as JSON.
 * @param {number} [timeout=5000] - Timeout in milliseconds. For backward compatibility.
 * @param {object} [options] - Additional request options.
 * @returns {object} Response result.
 */
httpclient.put = function (url, data, timeout = 5000, options = {}) {
    return httpclient.request({ ...options, url: url, method: 'PUT', body: data, timeout: timeout });
}

/**
 * Send PATCH request with a body.
 * @param {string} url - Request URL.
 * @param {string|object} data - Request body. JS objects will be stringified as JSON.
 * @param {number} [timeout=5000] - Timeout in milliseconds. For backward compatibility.
 * @param {object} [options] - Additional request options.
 * @returns {object} Response result.
 */
httpclient.patch = function (url, data, timeout = 5000, options = {}) {
    return httpclient.request({ ...options, url: url, method: 'PATCH', body: data, timeout: timeout });
}

/**
 * Send DELETE request.
 * @param {string} url - Request URL.
 * @param {number} [timeout=5000] - Timeout in milliseconds. For backward compatibility.
 * @param {object} [options] - Additional request options.
 * @returns {object} Response result.
 */
httpclient.delete = function (url, timeout = 5000, options = {}) {
    return httpclient.request({ ...options, url: url, method: 'DELETE', timeout: timeout });
}

/**
 * Download a file.
 * @param {string} url - Request URL.
 * @param {string} localPath - Local path to save the file.
 * @param {number} [timeout=30000] - Timeout in milliseconds. For backward compatibility.
 * @param {object} [options] - Additional request options.
 * @returns {object} Response result { code, status, message, headers } (no data field).
 *                   - headers: Empty object (download does not capture headers).
 */
httpclient.download = function (url, localPath, timeout = 30000, options = {}) {
    if (!url) throw new Error("URL is required");
    if (!localPath) throw new Error("Local path is required");

    const optsToSet = {
        ...options,
        timeout: timeout, // Longer default timeout for downloads
        url: url,
        method: 'GET'
    };

    // Convert headers object to array of strings for C layer
    if (optsToSet.headers && typeof optsToSet.headers === 'object' && !Array.isArray(optsToSet.headers)) {
        optsToSet.headers = Object.entries(optsToSet.headers).map(([key, value]) => `${key}: ${value}`);
    }

    return native.download(localPath, optsToSet);
}

/**
 * Upload a file.
 * @param {string} url - Request URL.
 * @param {string} localPath - Local path of the file to upload.
 * @param {number} [timeout=30000] - Timeout in milliseconds. For backward compatibility.
 * @param {object} [options] - Additional request options.
 * @returns {object} Response result { code, status, message, headers } (no data field).
 *                   - headers: Empty object (upload does not capture headers).
 */
httpclient.upload = function (url, localPath, timeout = 30000, options = {}) {
    if (!url) throw new Error("URL is required");
    if (!localPath) throw new Error("Local path is required");

    const optsToSet = {
        ...options,
        timeout: timeout, // Longer default timeout for uploads
        url: url,
        method: 'POST'
    };

    // Convert headers object to array of strings for C layer
    if (optsToSet.headers && typeof optsToSet.headers === 'object' && !Array.isArray(optsToSet.headers)) {
        optsToSet.headers = Object.entries(optsToSet.headers).map(([key, value]) => `${key}: ${value}`);
    }

    return native.upload(localPath, optsToSet);
}


// #region Deprecated Functions
// The following functions are deprecated and will be removed in a future version.
// They are kept for backward compatibility to guide users to the new stateless API.

const DEPRECATION_ERROR_MSG = "The stateful API (init, deinit, setOpt, reset) is deprecated. Please use the new stateless, function-based API by passing all options in a single object to methods like request(), get(), post(), etc.";

/**
 * @deprecated Use the new stateless API.
 */
httpclient.init = function () {
    // This function is now a no-op, but we don't throw an error
    // to allow old code to run without crashing, though it's discouraged.
}

/**
 * @deprecated Use the new stateless API.
 */
httpclient.deinit = function () {
    // This function is now a no-op.
}


/**
 * @deprecated Use the new stateless API by passing options to request() or other methods.
 */
httpclient.setOpt = function () {
    throw new Error(DEPRECATION_ERROR_MSG);
}

/**
 * @deprecated Use the new stateless API. Each call is already isolated.
 */
httpclient.reset = function () {
    throw new Error(DEPRECATION_ERROR_MSG);
}

// #endregion

export default httpclient;